package main

import "fmt"

func main() {
	// main方法充当客户端，创建并配置具体命令对象, 完成命令与执行操作的接收者的关联。
	cpu := CPU{}
	ps5 := PS5{make(map[string]Command)}
	ps5.SetCommand("a", NewACommand(&cpu, 1))
	ps5.SetCommand("b", NewBCommand(&cpu, "commandB", 2))
	ps5.DoCommand("c")
	ps5.DoCommand("b")
	ps5.DoCommand("a")
}

/*
命令模式是一种行为型模式。它通过将请求封装为一个独立的对象即命令对象，来解耦命令的调用者和接收者，
使得调用者和接收者不直接交互。在命令对象里会包含请求相关的全部信息，每一个命令都是一个操作的请求：
请求方发出请求要求执行一个操作； 接收方收到请求，并执行操作。
*/
/*
Receiver：命令的接收方，唯一包含业务逻辑的类，命令对象会将请求传递给它，它是请求的最终处理者
Command：命令对象，组装了一个Receiver成员，并绑定实现了Receiver的一个特定行为的调用
Invoker：请求的发送者，组装了Command成员，通过调用Command实例的execute()方法来触发对应的指令
Client：通过将Receiver实例和请求信息传递给Command构造器来创建Command对象，之后会将创建的对象同Invoker绑定。
*/

// CPU 命令的接收者，负责逻辑执行
type CPU struct{}

func (CPU) ADoSomething(param int) {
	fmt.Printf("a do something with param %v\n", param)
}
func (CPU) BDoSomething(param1 string, param2 int) {
	fmt.Printf("b do something with params %v and %v \n", param1, param2)
}
func (CPU) CDoSomething() {
	fmt.Println("c do something with no params")
}

// Command 接口中仅申明一个命令的执行方法Execute
type Command interface {
	Execute()
}

// ACommand 命令对象持有一个指向接收者的引用，以及请求中的所有参数
type ACommand struct {
	cpu   *CPU
	param int
}

// Execute 命令对象不会进行逻辑处理，而调用Execute方法会将发送者的请求委派给接收者对象
func (a *ACommand) Execute() {
	a.cpu.ADoSomething(a.param)
	a.cpu.CDoSomething()
}

func NewACommand(cpu *CPU, param int) Command {
	return &ACommand{
		cpu:   cpu,
		param: param,
	}
}

type BCommand struct {
	state  bool
	cpu    *CPU
	param1 string
	param2 int
}

func (b *BCommand) Execute() {
	if b.state {
		return
	}
	b.cpu.BDoSomething(b.param1, b.param2)
	b.state = true
	b.cpu.CDoSomething()
}

func NewBCommand(cpu *CPU, param1 string, param2 int) Command {
	return &BCommand{
		state:  false,
		cpu:    cpu,
		param1: param1,
		param2: param2,
	}
}

type PS5 struct {
	commands map[string]Command
}

// SetCommand 方法将command指令设定给ps5
func (p *PS5) SetCommand(name string, command Command) {
	p.commands[name] = command
}

// DoCommand 方法选择要执行的命令
func (p *PS5) DoCommand(name string) {
	com, ok := p.commands[name]
	if !ok {
		fmt.Println("not find command")
	} else {
		com.Execute()
	}

}

/*
命令模式的优点

通过引入中间件（抽象接口），解耦了命令请求与实现。
扩展性良好，可以很容易地增加新命令。
支持组合命令，支持命令队列。
可以在现有命令的基础上，增加额外功能。比如日志记录，结合装饰器模式会更加灵活。
命令模式的缺点

具体命令类可能过多。
命令模式的结果其实就是接收方的执行结果，但是为了以命令的形式进行架构、解耦请求与实现，引入了额外类型结构（引入了请求方与抽象命令接口），增加了理解上的困难。
*/
